package sm.codec;

import java.util.ArrayList;
import java.util.List;

import org.apache.mina.core.buffer.IoBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sm.codec.api.Codec;
import sm.codec.api.CodecFactory;
import sm.codec.exception.DecodeException;
import sm.codec.exception.EncodeException;
import sm.codec.type.NullValue;
import sm.codec.type.Struct;
import sm.def.Type;

public abstract class AbstractStructCodec {
	private static Logger logger = LoggerFactory.getLogger(Codec.class);

	protected Object decode(IoBuffer buffer, int type) throws DecodeException {
		byte typeByte = (byte) (type << 4);
		byte start = typeByte;
		byte end = (byte) (typeByte | 0x01);
		byte nullValue = (byte) (typeByte | 0x0F);

		List<Object> objects = new ArrayList<Object>();

		byte cur = buffer.get();
		int curPos = buffer.position();

		if (cur == end) {
			return objects;
		} else if (cur == nullValue) {
			cur = buffer.get();
			if (cur != end) {
				buffer.position(curPos);
			}
			return NullValue.RecordNull;
		} else if (cur == start) {
			cur = buffer.get();
			while (cur != end) {
				int fieldType = (cur & 0xFF) >>> 4;
				buffer.position(curPos);
				Object obj = CodecFactory.getCodec(fieldType).decode(buffer);
				if (logger.isTraceEnabled()) {
					logger.trace("decoded object: " + obj);
				}
				objects.add(obj);
				curPos = buffer.position();
				cur = buffer.get();
			}
			return new Struct(objects, type);
		} else {
			throw new DecodeException("The composite type " + start
					+ " can't be decoded.", buffer);
		}
	}

	protected void encode(IoBuffer buffer, Object obj, int type)
			throws EncodeException {
		byte typeByte = (byte) (type << 4);
		byte start = typeByte;
		byte end = (byte) (typeByte | 0x01);

		buffer.put(start);

		if (obj instanceof Struct) {
			Struct struct = (Struct) obj;
			List<Object> values = struct.getValue();
			for (Object value : values) {
				if (value == null) {
					buffer.put((byte) 0x0F);
				} else {
					int fieldType = Type.getTypeNumber(value);
					CodecFactory.getCodec(fieldType).encode(buffer, value);
				}
			}
		}

		buffer.put(end);

	}
}
